/*
 * Copyright 2010 Nicholas Alexander Cross
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.intellij.lang.plist.fileTypes;

import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.formatter.common.AbstractBlock;
import org.intellij.lang.plist.parser.PListElementTypes;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

public class PListFormattingModelBuilder implements FormattingModelBuilder {

    @NotNull
    public FormattingModel createModel(PsiElement element, CodeStyleSettings codeStyleSettings) {
        final PsiFile containingFile = element.getContainingFile();
        ASTNode node = containingFile.getNode();

        return FormattingModelProvider.createFormattingModelForPsiFile(
                containingFile,
                new PListBlock(node, Wrap.createWrap(Wrap.ALWAYS, true)),
                null
        );
    }

    public TextRange getRangeAffectingIndent(PsiFile psiFile, int i, ASTNode astNode) {
        return null;
    }

    private class PListBlock extends AbstractBlock {

        protected PListBlock(ASTNode astNode, final Wrap wrap) {
            super(astNode, wrap, Alignment.createAlignment());
        }

        @Override
        protected List<Block> buildChildren() {
            List<Block> blocks = new ArrayList<Block>();
            final ASTNode[] blockNodes = this.myNode.getChildren(null);

            for (int i = 0, blockNodesLength = blockNodes.length; i < blockNodesLength; i++) {
                ASTNode node = blockNodes[i];
                if (node.getText().trim().length() > 0) {
                    if (PListElementTypes.BLOCKS.contains(node.getElementType())) {
                        blocks.add(new PListBlock(node, Wrap.createWrap(Wrap.ALWAYS, true)));
                    } else if (PListElementTypes.RIGHT_BRACE == node.getElementType() || PListElementTypes.RIGHT_PARENTHESES == node.getElementType()) {
                        PListBlock block = null;

                        if (i < 1 || shouldWrapNode(blockNodes, i - 1)) {
                            block = new PListBlock(node, Wrap.createWrap(Wrap.ALWAYS, true)) {
                                @Override
                                public Indent getIndent() {
                                    return Indent.getNoneIndent();
                                }
                            };
                        }
                        else {
                            block = new PListBlock(node, Wrap.createWrap(Wrap.NONE, false));
                        }

                        blocks.add(block);
                    } else {
                        blocks.add(new PListBlock(node, Wrap.createWrap(Wrap.NONE, false)));
                    }
                }
            }

            return blocks;
        }

        private boolean shouldWrapNode(ASTNode[] blockNode, int index) {
            int i = index;

            if (PListElementTypes.WHITE_SPACE_TOKENS.contains(blockNode[i].getElementType()) && i > 0) {
                i--;
            }

            return blockNode[i].getElementType() != PListElementTypes.LEFT_BRACE && blockNode[i].getElementType() != PListElementTypes.LEFT_PARENTHESES;
        }

        public Spacing getSpacing(Block block, Block block1) {
            return Spacing.createSpacing(0, 1, 1, false, 1, 1);
        }

        public boolean isLeaf() {
            return false;
        }
    }


}
